package excelrept

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/xuri/excelize/v2"
	"shqsoft.cn/pas3ui/base"
)

//ExpCalc 表达式计算
type ExpCalc struct {
	tableItem *TableItem
}

//NewExpCalc 创建计算对象
func NewExpCalc(tableItem *TableItem) *ExpCalc {
	return &ExpCalc{
		tableItem: tableItem,
	}
}
func parseConditionExp(exp string) (lexp string, rexp string, ralation string, err error) {
	sm := map[int]string{1: "有效字符", 2: "=", 3: ">", 4: "<"} //状态标识
	stat := 1
	split := 0
	hasTwo := false
	st := []int{1, 2}
	sc := map[int][]int{1: {1, 2, 3, 4}, 2: {1}, 3: {2, 1}, 4: {2, 1}}
	for i := 1; i < len(exp); i++ {
		b := exp[i]
		isOk := false
		for _, s := range st {
			if s == 1 && isValidToken(b) {
				stat = 1
				st = sc[1]
				isOk = true
				break
			} else if s == 2 && isEqual(b, '=') {
				if stat == 1 {
					split = i
				}
				if stat == 3 || stat == 4 {
					hasTwo = true
				}
				stat = 2
				st = sc[2]
				isOk = true
				break
			} else if s == 3 && isEqual(b, '>') {
				if stat == 1 {
					split = i
				}
				stat = 3
				st = sc[3]
				isOk = true
				break
			} else if s == 4 && isEqual(b, '<') {
				if stat == 1 {
					split = i
				}
				stat = 4
				st = sc[3]
				isOk = true
				break
			}
		}
		if !isOk {
			msg := ""
			for i, s := range st {
				msg += sm[s]
				if i < len(st)-1 {
					msg += "或者"
				}
			}
			err = fmt.Errorf("错误的表达式%s，表达式的第%d位必须是%s", exp, i, msg)
			return
		}
	}
	if split == 0 {
		err = fmt.Errorf("错误的条件表达式%s，没有找到比较关系符", exp)
		return
	}
	expBs := []byte(exp)
	if hasTwo {
		ralation = string([]byte{exp[split], '='})
		lexp = string(expBs[0:split])
		rexp = string(expBs[split+2:])
	} else {
		ralation = string([]byte{exp[split]})
		lexp = string(expBs[0:split])
		rexp = string(expBs[split+1:])
	}
	return
}
func (e *ExpCalc) parseCalcExp(exp string) (cellExps []string, minus []int, sumIndexs []int, err error) {
	keys := map[string]int{"[": 1, "]": 2, "sum": 3, "(": 4, ")": 5, "+": 6, "-": 6}
	keys2 := map[int]string{1: "[", 2: "]", 3: "sum", 4: "(", 5: ")", 6: "+,-"}
	sm := map[int]int{1: 2, 3: 4, 4: 5, 2: 6, 5: 6, 6: 1}
	ebs := []byte(exp)
	cellExps = make([]string, 0, 10)
	cellExp := make([]byte, 0, 10)
	var tstr string
	stat := 1  //下一个要找的字符
	var st int //当前字符
	index := 0
	minus = make([]int, 0, 5)
	sumIndexs = make([]int, 0, 5)
	i := 0
	for i < len(ebs) {
		b := ebs[i]
		if b == ' ' {
			continue
		}
		tstr = string(b)
		st = keys[tstr]
		if st != stat {
			if stat == 2 || stat == 5 {
				cellExp = append(cellExp, b)
			} else if strings.ToLower(string(ebs[i:i+3])) == "sum" {
				stat = 4
				i += 3
				continue
			} else {
				err = fmt.Errorf("错误的计算表达式%s，表达式的第%d位应该是%s", exp, i, keys2[stat])
				return
			}
		} else {
			if st == 2 || st == 5 {
				cellExps = append(cellExps, string(cellExp))
				if st == 5 {
					sumIndexs = append(sumIndexs, index)
				}
				index++
				cellExp = make([]byte, 0, 10)
			} else if st == 6 {
				if b == '-' {
					minus = append(minus, index)
				}
			}
			stat = sm[st]
		}
		i++
	}
	if stat == 2 || stat == 4 {
		err = fmt.Errorf("错误的计算表达式%s，没有找到结束符号%s", exp, keys2[stat])
		return
	}
	return
}

//CalcReptExp 计算报表表达式(按照银监局的校验规则公式)
func (e *ExpCalc) CalcReptExp(exp, rg string) (valMap map[string]float64, fcell string, err error) {
	cellExps, minus, sumIndexs, err := e.parseCalcExp(exp)
	if err != nil {
		return
	}
	var rgs []string
	tp := getExpType(exp)
	var fc bool
	rgs, fc, err = e.parseRange(tp, rg)
	if err != nil {
		return
	}
	if fc {
		err = fmt.Errorf("错误的范围表达式%s，不能同时指定行和列", rg)
		return
	}
	valMap = make(map[string]float64)
	var cell string
	for j, rge := range rgs {
		var vi *float64
		var val float64
		for i, ce := range cellExps {
			if base.IsInSliceForInt(sumIndexs, i) {
				vi, cell, err = e.calcSumExp(ce, rge)
			} else {
				vi, cell, err = e.calcReptCell(ce, rge)
			}
			if j == 0 && i == 0 {
				fcell = cell
			}
			if err != nil {
				return
			}
			if vi == nil {
				continue
			}
			if base.IsInSliceForInt(minus, i) {
				val -= *vi
			} else {
				val += *vi
			}
		}
		valMap[rge] = val
	}
	return
}
func (e *ExpCalc) parseRange(tp, rg string) (rgs []string, fullCell bool, err error) {
	if tp == "00" {
		err = fmt.Errorf("错误的表达式%s，表达式应该含有字母或者数字", rg)
		return
	} else if tp == "11" && rg == "" {
		rgs = []string{""}
		return
	}
	isCol := true
	if tp == "10" {
		isCol = false
	}
	rgs = make([]string, 0)
	if len(rg) == 0 {
		if isCol {
			len := e.tableItem.DataRange.ECol - e.tableItem.RowRange.ECol
			var colStr string
			for i := 1; i <= len; i++ {
				colStr, err = excelize.ColumnNumberToName(i)
				if err != nil {
					return
				}
				rgs = append(rgs, colStr)
			}
		} else {
			len := e.tableItem.DataRange.ERow - e.tableItem.DataRange.SRow + 1
			for i := 1; i <= len; i++ {
				rgs = append(rgs, strconv.Itoa(i))
			}
		}
		return
	}
	sm := map[int]string{1: "数字", 2: "点", 3: "字母", 4: ":", 5: "[", 6: "]"} //状态标识
	var stat int                                                           //当前状态
	var st []int                                                           //当前可转化状态
	var sc map[int][]int                                                   //可转换map
	rgArr := strings.Split(rg, ",")
	var index int
	var expBytes []byte
	var expBytesArr [][]byte
	for _, rgExp := range rgArr {
		if isSquareStart(rgExp[0]) {
			if isCol {
				stat = 0
				st = []int{5}
				sc = map[int][]int{0: {5}, 5: {3}, 3: {1, 3, 4, 6}, 1: {1, 2, 4, 6}, 2: {1, 6}, 4: {5}, 6: {4}}
			} else {
				stat = 0
				st = []int{5}
				sc = map[int][]int{0: {5}, 5: {1}, 1: {1, 2, 3, 4, 6}, 2: {1, 3, 6}, 3: {3, 4, 6}, 4: {5}, 6: {4}}
			}
		} else {
			if isCol {
				stat = 0
				st = []int{3}
				sc = map[int][]int{0: {3}, 3: {1, 3, 4}, 1: {1, 2, 4}, 2: {1}, 4: {3}}
			} else {
				stat = 0
				st = []int{1}
				sc = map[int][]int{0: {1}, 1: {1, 2, 3, 4}, 2: {1, 3}, 3: {3, 4}, 4: {1}}
			}
		}
		index = 0
		expBytesArr = make([][]byte, 0, 2)
		splits := make([]int, 2)
		sn := 0
		expBytes = make([]byte, 0, 10)
		for i := 0; i < len(rgExp); i++ {
			b := rgExp[i]
			isOk := false
			for _, s := range st {
				if s == 1 && isNumber(b) {
					if stat == 3 {
						fullCell = true
						splits[index] = i - sn
					}
					stat = 1
					st = sc[1]
					isOk = true
					break
				} else if s == 2 && isDot(b) {
					if stat == 3 {
						fullCell = true
						splits[index] = i - sn
					}
					stat = 2
					st = sc[2]
					isOk = true
					break
				} else if s == 3 && isLetter(b) {
					if stat == 1 {
						fullCell = true
						splits[index] = i - sn
					}
					stat = 3
					st = sc[3]
					isOk = true
					break
				} else if s == 4 && isColon(b) {
					stat = 4
					st = sc[4]
					isOk = true
					break
				} else if s == 5 && isSquareStart(b) {
					stat = 5
					st = sc[5]
					isOk = true
					break
				} else if s == 6 && isSquareEnd(b) {
					stat = 6
					st = sc[6]
					isOk = true
					break
				}
			}
			if !isOk {
				msg := ""
				for i, s := range st {
					msg += sm[s]
					if i < len(st)-1 {
						msg += "或者"
					}
				}
				err = fmt.Errorf("错误的表达式%s，表达式的第%d位必须是%s", rgExp, i, msg)
				return
			}
			if isCol && stat == 3 {
				sn = i
			} else if !isCol && stat == 1 {
				sn = i
			}
			if isNumber(b) || isDot(b) || isLetter(b) {
				expBytes = append(expBytes, b)
			}
			if stat == 4 {
				expBytesArr = append(expBytesArr, expBytes)
				expBytes = make([]byte, 0, 10)
				index++
			}
			if index > 1 {
				err = fmt.Errorf("错误的表达式%s，表达式的分割符最多只能有一个", rgExp)
				return
			}
		}
		expBytesArr = append(expBytesArr, expBytes)
		if len(expBytesArr) == 1 {
			rgs = append(rgs, string(expBytes))
		} else {
			e1 := expBytesArr[0]
			e2 := expBytesArr[1]
			if fullCell {
				var col1, col2, row1, row2 int
				if isCol {
					col1, err = excelize.ColumnNameToNumber(string(e1[0:splits[0]]))
					if err != nil {
						return
					}
					row1, err = strconv.Atoi(string(e1[splits[0]:]))
					if err != nil {
						return
					}
					col2, err = excelize.ColumnNameToNumber(string(e2[0:splits[1]]))
					if err != nil {
						return
					}
					row2, err = strconv.Atoi(string(e2[splits[1]:]))
					if err != nil {
						return
					}
				} else {
					col1, err = e.markToNo(string(e1[0:splits[0]]))
					if err != nil {
						return
					}
					row2, err = excelize.ColumnNameToNumber(string(e2[splits[1]:]))
					if err != nil {
						return
					}
					col2, err = e.markToNo(string(e2[0:splits[1]]))
					if err != nil {
						return
					}
				}
				if col1 > col2 || row1 > row2 {
					err = fmt.Errorf("错误的范围表达式%s,分割符后面的部分应该大于前面的部分", rg)
				}
				var rg2 string
				for i := col1; i <= col2; i++ {
					rg2, err = excelize.ColumnNumberToName(i)
					if err != nil {
						return
					}
					for j := row1; j <= row2; j++ {
						rgs = append(rgs, rg2+strconv.Itoa(j))
					}
				}
			} else {
				var start, ent int
				if isCol {
					e1Str := string(e1)
					start, err = excelize.ColumnNameToNumber(e1Str)
					if err != nil {
						return
					}
					ent, err = excelize.ColumnNameToNumber(string(e2))
					if err != nil {
						return
					}
				} else {
					start, err = e.markToNo(string(e1))
					if err != nil {
						return
					}
					ent, err = e.markToNo(string(e2))
					if err != nil {
						return
					}
				}
				if ent < start {
					err = fmt.Errorf("错误的范围表达式%s,分割符后面的部分应该大于前面的部分", rg)
				}
				var rg2 string
				for i := start; i <= ent; i++ {
					if isCol {
						rg2, err = excelize.ColumnNumberToName(i)
						if err != nil {
							return
						}
					} else {
						rg2 = strconv.Itoa(i)
					}
					rgs = append(rgs, rg2)
				}
			}
		}
	}
	return
}
func (e *ExpCalc) calcSumExp(exp, rg string) (val *float64, fcell string, err error) {
	var expcs []string
	var fc bool
	tp := getExpType(exp)
	expcs, fc, err = e.parseRange(tp, exp)
	if err != nil {
		return
	}
	if fc {
		rg = ""
	}
	var vi *float64
	var sum float64
	var cell string
	for i, expc := range expcs {
		vi, cell, err = e.calcReptCell(expc, rg)
		if err != nil {
			return
		}
		if vi == nil {
			continue
		}
		if i == 0 {
			fcell = cell
		}
		sum += *vi
	}
	val = &sum
	return
}
func (e *ExpCalc) calcReptCell(exp, rg string) (val *float64, cell string, err error) {
	sm := map[int]string{1: "数字", 2: "点", 3: "字母"} //状态标识
	var stat int                                   //当前状态
	var st []int                                   //当前可转化状态
	var sc map[int][]int                           //可转换map
	if len(exp) == 0 {
		err = fmt.Errorf("表达式不能为空！")
		return
	}
	fb := []byte(exp)[0]
	numSt := false
	if isNumber(fb) {
		numSt = true
	} else if isLetter(fb) {
		numSt = false
	} else {
		err = fmt.Errorf("表达式%s错误，第一位必须是字母或者数字！", exp)
		return
	}
	if rg == "" {
		if numSt {
			stat = 1
			st = []int{1, 2, 3}
			sc = map[int][]int{1: {1, 2, 3}, 2: {1, 3}, 3: {3}}
		} else {
			stat = 3
			st = []int{3, 1}
			sc = map[int][]int{3: {3, 1}, 1: {1, 2}, 2: {1}}
		}
	} else {
		fb = []byte(rg)[0]
		ns := false
		if isNumber(fb) {
			ns = true
		} else if isLetter(fb) {
			ns = false
		} else {
			err = fmt.Errorf("范围表达式%s错误，第一位必须是字母或者数字", rg)
			return
		}
		if numSt && ns {
			err = fmt.Errorf("表达式%s是行上的计算，范围表达式%s应该表达列，而不是表达行", exp, rg)
			return
		} else if !numSt && !ns {
			err = fmt.Errorf("表达式%s是列上的计算，范围表达式%s应该表达行，而不是表达列", exp, rg)
			return
		}
		if numSt {
			stat = 1
			st = []int{1, 2}
			sc = map[int][]int{1: {1, 2}, 2: {1}}
		} else {
			stat = 3
			st = []int{3}
			sc = map[int][]int{3: {3}}
		}
	}
	split := 0
	for i := 1; i < len(exp); i++ {
		b := exp[i]
		isOk := false
		for _, s := range st {
			if s == 1 && isNumber(b) {
				//字母和数字的分解线
				if stat == 3 {
					split = i
				}
				stat = 1
				st = sc[1]
				isOk = true
				break
			} else if s == 2 && isDot(b) {
				stat = 2
				st = sc[2]
				isOk = true
				break
			} else if s == 3 && isLetter(b) {
				//数字和字母的分解线
				if stat == 1 || stat == 2 {
					split = i
				}
				stat = 3
				st = sc[3]
				isOk = true
				break
			}
		}
		if !isOk {
			msg := ""
			for i, s := range st {
				msg += sm[s]
				if i < len(st)-1 {
					msg += "或者"
				}
			}
			err = fmt.Errorf("错误的表达式%s，表达式的第%d位必须是%s", exp, i, msg)
			return
		}
	}
	if rg == "" && split == 0 {
		err = fmt.Errorf("错误的表达式%s，没有为表达式指定范围或者表达式不完整", exp)
		return
	}
	var col, row string
	if rg == "" {
		expBs := []byte(exp)
		e1 := expBs[0:split]
		e2 := expBs[split:]
		if numSt {
			row = string(e1)
			col = string(e2)
		} else {
			row = string(e2)
			col = string(e1)
		}
	} else {
		if numSt {
			row = exp
			col = rg
		} else {
			col = exp
			row = rg
		}
	}
	return e.getReptCellVal(col, row)
}
func (e *ExpCalc) getReptCellVal(col string, row string) (val *float64, cell string, err error) {
	cnum, err := excelize.ColumnNameToNumber(col)
	if err != nil {
		return
	}
	cnum += e.tableItem.RowRange.ECol
	var colStr string
	rnum, err := e.markToNo(row)
	if err != nil {
		return
	}
	colStr, err = excelize.ColumnNumberToName(cnum)
	if err != nil {
		return
	}
	cell = colStr + strconv.Itoa(rnum)
	vstr, ok := e.tableItem.DataCells[cell]
	if ok {
		if vstr != "" {
			if strings.HasSuffix(vstr, "%") {
				vstr = vstr[:len(vstr)-1] + "00"
			}
			vfl, err := strconv.ParseFloat(vstr, 64)
			if err != nil {
				return nil, "", err
			}
			val = &vfl
		}
	}
	return
}
func (e *ExpCalc) markToNo(mark string) (rnum int, err error) {
	dotIdx := strings.LastIndex(mark, ".")
	if dotIdx == -1 {
		rnum, err = strconv.Atoi(mark)
		if err != nil {
			return
		}
		rnum = rnum + e.tableItem.DataRange.SRow - 1
	} else {
		var colStr, cell, caption string
		colIdx := e.tableItem.RowRange.ECol
		colStr, err = excelize.ColumnNumberToName(colIdx)
		if err != nil {
			return
		}
		for i := e.tableItem.DataRange.SRow; i <= e.tableItem.DataRange.ERow; i++ {
			cell = colStr + strconv.Itoa(i)
			caption = strings.TrimSpace(e.tableItem.DataCells[cell])
			if strings.Index(caption, mark) == 0 || strings.Index(caption, "其中："+mark) == 0 {
				rnum = i
				break
			}
		}
		if rnum == 0 {
			err = fmt.Errorf("行标识符%s在报表中没有找到", mark)
			return
		}
	}
	return
}

//isNumber 确定得到的token是整数类型
func isNumber(token uint8) bool {
	if token <= uint8('9') && token >= uint8('0') {
		return true
	}
	return false
}

func isEqual(token1 uint8, token2 uint8) bool {
	return token1 == token2
}

//isDot 确定得到的token是点
func isDot(token uint8) bool {
	return token == uint8('.')
}
func isColon(token uint8) bool {
	if token == uint8('-') || token == uint8(':') {
		return true
	}
	return false
}

//isLetter 确定是范围内a-z或者A-Z
func isLetter(token uint8) bool {
	if (token <= uint8('z') && token >= uint8('a')) || (token <= uint8('Z') && token >= uint8('A')) {
		return true
	}
	return false
}

//isOper 是否运算符
func isOper(token uint8) bool {
	if token == uint8('+') || token == uint8('-') || token == uint8('*') || token == uint8('/') {
		return true
	}
	return false
}
func isValidToken(token uint8) bool {
	if token == uint8('[') || token == uint8(']') || token == uint8('(') || token == uint8(')') || token == uint8('+') || token == uint8('-') {
		return true
	}
	if isNumber(token) || isDot(token) || isColon(token) || isLetter(token) {
		return true
	}
	return false
}
func isSquareStart(token uint8) bool {
	return token == uint8('[')
}
func isSquareEnd(token uint8) bool {
	return token == uint8(']')
}
func getExpType(exp string) string {
	var hasCol, hasRow bool
	for i := 0; i < len(exp); i++ {
		b := exp[i]
		if isLetter(b) {
			hasCol = true
		} else if isNumber(b) {
			hasRow = true
		}
	}
	if hasCol && hasRow {
		return "11"
	} else if hasCol {
		return "10"
	} else if hasRow {
		return "01"
	}
	return "00"
}
